/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

package io.netty.buffer;

import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
import static java.lang.Math.max;

import io.netty.util.internal.LongCounter;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.StringUtil;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

abstract class PoolArena<T> implements PoolArenaMetric {

  static final boolean HAS_UNSAFE = PlatformDependent.hasUnsafe();

  enum SizeClass {
    Tiny,
    Small,
    Normal
  }

  // 这里512 >>> 4的结果其实就是512除以16，结果为32，表示tiny类型的缓冲池有32个
  static final int numTinySubpagePools = 512 >>> 4;

  final PooledByteBufAllocator parent;

  private final int maxOrder;
  final int pageSize;
  final int pageShifts;
  final int chunkSize;
  final int subpageOverflowMask;  // -8192 => 1111 1111 1111 1111 1110 0000 0000 0000
  final int numSmallSubpagePools;
  final int directMemoryCacheAlignment;
  final int directMemoryCacheAlignmentMask;
  private final PoolSubpage<T>[] tinySubpagePools;
  private final PoolSubpage<T>[] smallSubpagePools;

  private final PoolChunkList<T> q050;
  private final PoolChunkList<T> q025;
  private final PoolChunkList<T> q000;
  private final PoolChunkList<T> qInit;
  private final PoolChunkList<T> q075;
  private final PoolChunkList<T> q100;

  private final List<PoolChunkListMetric> chunkListMetrics;

  // Metrics for allocations and deallocations
  private long allocationsNormal;
  // We need to use the LongCounter here as this is not guarded via synchronized block.
  private final LongCounter allocationsTiny = PlatformDependent.newLongCounter();
  private final LongCounter allocationsSmall = PlatformDependent.newLongCounter();
  private final LongCounter allocationsHuge = PlatformDependent.newLongCounter();
  private final LongCounter activeBytesHuge = PlatformDependent.newLongCounter();

  private long deallocationsTiny;
  private long deallocationsSmall;
  private long deallocationsNormal;

  // We need to use the LongCounter here as this is not guarded via synchronized block.
  private final LongCounter deallocationsHuge = PlatformDependent.newLongCounter();

  // Number of thread caches backed by this arena.
  // 这里numThreadCaches记录了当前Arena中记录了的threadCache的数量
  final AtomicInteger numThreadCaches = new AtomicInteger();

  // TODO: Test if adding padding helps under contention
  //private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7;

  protected PoolArena(PooledByteBufAllocator parent, int pageSize,
      int maxOrder, int pageShifts, int chunkSize, int cacheAlignment) {
    this.parent = parent;   // ByteBufAllocator
    this.pageSize = pageSize;   // 8KB
    this.maxOrder = maxOrder;   // 11  树的层数
    this.pageShifts = pageShifts;   // 13   pageSize的偏移量，2^13，因而为13
    this.chunkSize = chunkSize; // 一个chunk的大小，为16M
    // 这里directMemoryCacheAlignment默认为0，如果大于0，则其值为2的指数次方
    directMemoryCacheAlignment = cacheAlignment;    // 指定了directMemory中前多少位用于自定义使用
    // 这里directMemoryCacheAlignmentMask的值默认为-1，如果大于-1，那么其值的二进制为中全部都是1，
    // 这里directMemoryCacheAlignmentMask即使为-1时，其二进制表示也全都是1
    directMemoryCacheAlignmentMask = cacheAlignment - 1;
    // 这里该计算式其实就是将pageSize进行取反，比如这里pageSize默认为8KB，其表示形式为
    // 00000000 00000000 00011111 11111111
    // 经过该运算之后的值为
    // 11111111 11111111 11100000 00000000
    // 这个参数的主要作用是，将其与目标容量进行"&"操作，如果结果等于0，则说明目标容量是小于pageSize的，也就是8KB，
    // 从而可以判断目标容量是否在某个我们已经声明的缓存池内
    subpageOverflowMask = ~(pageSize - 1);
    // 这里numTinySubpagePools的大小为32，表示了tiny类型的缓存池个数为32，
    // tinySubpagePools是PoolSubpage的一个数组，用于表示tiny类型的缓存池
    tinySubpagePools = newSubpagePoolArray(numTinySubpagePools);
    for (int i = 0; i < tinySubpagePools.length; i++) {
      // 创建tiny类型的缓存池，初始状态时，其头结点的前置指针和后置指针都是指向的当前节点，也即头结点
      tinySubpagePools[i] = newSubpagePoolHead(pageSize);
    }

    // numSmallSubpagePools指定了small类型的缓存池的个数，其值为4
    numSmallSubpagePools = pageShifts - 9;
    // 这里smallSubpagePools就是一个PoolSubpage类型的数组，用于表示small类型的缓存池
    smallSubpagePools = newSubpagePoolArray(numSmallSubpagePools);
    for (int i = 0; i < smallSubpagePools.length; i++) {
      // 初始化small类型的缓存池，同样的，初始时其头结点的前置指针和后置指针都是指向的头结点
      smallSubpagePools[i] = newSubpagePoolHead(pageSize);
    }

    // q100的maxCapacity为0，也就是说基本不能从其中获取任何的内存
    q100 = new PoolChunkList<T>(this, null, 100, Integer.MAX_VALUE, chunkSize);
    // q75的maxCapacity为25%，也就是说可以从其中获取的最大内存为 16M*25%=4M
    q075 = new PoolChunkList<T>(this, q100, 75, 100, chunkSize);
    // q75的maxCapacity为50%，也就是说可以从其中获取的最大内存为 16M*50%=8M
    q050 = new PoolChunkList<T>(this, q075, 50, 100, chunkSize);
    // q75的maxCapacity为75%，也就是说可以从其中获取的最大内存为 16M*75%=12M
    q025 = new PoolChunkList<T>(this, q050, 25, 75, chunkSize);
    // q75的maxCapacity为99%，也就是说可以从其中获取的最大内存为 16M*99%≈16M
    q000 = new PoolChunkList<T>(this, q025, 1, 50, chunkSize);
    // 这里虽然minUsage传如的是一个负数，但是内部会将其与1进行比较，取较大的一个，因而这里minUsage是一个负数，
    // 但是最大容量还是99%，也即16M*99%≈16M
    qInit = new PoolChunkList<T>(this, q000, Integer.MIN_VALUE, 25, chunkSize);

    // 这里将这些PoolChunkList按照链表的方式进行关联，关联的顺序如下：
    // qInit->q000->q025->q050->q075->q100
    q100.prevList(q075);
    q075.prevList(q050);
    q050.prevList(q025);
    q025.prevList(q000);
    q000.prevList(null);
    qInit.prevList(qInit);

    List<PoolChunkListMetric> metrics = new ArrayList<PoolChunkListMetric>(6);
    metrics.add(qInit);
    metrics.add(q000);
    metrics.add(q025);
    metrics.add(q050);
    metrics.add(q075);
    metrics.add(q100);
    chunkListMetrics = Collections.unmodifiableList(metrics);
  }

  private PoolSubpage<T> newSubpagePoolHead(int pageSize) {
    PoolSubpage<T> head = new PoolSubpage<T>(pageSize);
    head.prev = head;
    head.next = head;
    return head;
  }

  @SuppressWarnings("unchecked")
  private PoolSubpage<T>[] newSubpagePoolArray(int size) {
    return new PoolSubpage[size];
  }

  abstract boolean isDirect();

  PooledByteBuf<T> allocate(PoolThreadCache cache, int reqCapacity, int maxCapacity) {
    // 这里newByteBuf()方法将会创建一个PooledByteBuf对象，但是该对象是未经初始化的，也就是说其内部的ByteBuffer
    // 和readerIndex，writerIndex等参数都是默认值
    PooledByteBuf<T> buf = newByteBuf(maxCapacity);
    // 使用对应的方式为创建的ByteBuf初始化相关内存数据
    allocate(cache, buf, reqCapacity);
    return buf;
  }

  static int tinyIdx(int normCapacity) {
    return normCapacity >>> 4;  // 这种计算就是将容量除以16，以便得到其应该使用多少个16KB
  }

  static int smallIdx(int normCapacity) {
    int tableIdx = 0;
    int i = normCapacity >>> 10;
    while (i != 0) {
      i >>>= 1;
      tableIdx++;
    }
    return tableIdx;
  }

  // capacity < pageSize
  boolean isTinyOrSmall(int normCapacity) {
    return (normCapacity & subpageOverflowMask) == 0;   // 判断当前容量是否小于8KB
  }

  // normCapacity < 512
  static boolean isTiny(int normCapacity) {
    return (normCapacity & 0xFFFFFE00) == 0;    // 判断当前容量是否小于512  这里0xFFFFFE00 = -512 = 11111111111111111111111000000000
  }

  private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) {
    // 这里normalizeCapacity()方法的主要作用是对目标容量进行规整操作，主要规则如下：
    // 1. 如果目标容量小于16字节，则返回16；
    // 2. 如果目标容量大于16字节，小于512字节，则以16字节为单位，返回大于目标字节数的第一个16字节的倍数。
    //    比如申请的100字节，那么大于100的16的倍数是112，因而返回112个字节
    // 3. 如果目标容量大于512字节，则返回大于目标容量的第一个2的指数幂。比如申请的1000字节，那么返回的将是1024
    final int normCapacity = normalizeCapacity(reqCapacity);
    // 判断目标容量是否小于8KB，小于8KB则使用tiny或small的方式申请内存
    if (isTinyOrSmall(normCapacity)) { // capacity < pageSize
      int tableIdx;
      PoolSubpage<T>[] table;
      boolean tiny = isTiny(normCapacity);  // 判断目标容量是否小于512字节，小于512字节的为tiny类型的
      if (tiny) { // < 512
        // 申请tiny类型的内存
        if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {
          // was able to allocate out of the cache so move on
          return;
        }
        tableIdx = tinyIdx(normCapacity);
        table = tinySubpagePools;
      } else {
        if (cache.allocateSmall(this, buf, reqCapacity, normCapacity)) {
          // was able to allocate out of the cache so move on
          return;
        }
        tableIdx = smallIdx(normCapacity);
        table = smallSubpagePools;
      }

      final PoolSubpage<T> head = table[tableIdx];

      /**
       * Synchronize on the head. This is needed as {@link PoolChunk#allocateSubpage(int)} and
       * {@link PoolChunk#free(long)} may modify the doubly linked list as well.
       */
      /**
       * 这里需要注意的是，由于对head进行了加锁，而在同步代码块中判断了s != head，也就是说PoolSubpage链表中是
       * 存在未使用的PoolSubpage的，因为如果该节点已经用完了，其是会被移除当前链表的。也就是说只要s != head，
       * 那么这里的allocate()方法就一定能够申请到所需要的内存块
       */
      synchronized (head) {
        final PoolSubpage<T> s = head.next;
        if (s != head) {
          assert s.doNotDestroy && s.elemSize == normCapacity;
          long handle = s.allocate();
          assert handle >= 0;
          s.chunk.initBufWithSubpage(buf, null, handle, reqCapacity);
          incTinySmallAllocation(tiny);
          return;
        }
      }
      synchronized (this) {
        allocateNormal(buf, reqCapacity, normCapacity);
      }

      incTinySmallAllocation(tiny);
      return;
    }
    if (normCapacity <= chunkSize) {
      if (cache.allocateNormal(this, buf, reqCapacity, normCapacity)) {
        // was able to allocate out of the cache so move on
        return;
      }
      synchronized (this) {
        allocateNormal(buf, reqCapacity, normCapacity);
        ++allocationsNormal;
      }
    } else {
      // Huge allocations are never served via the cache so just call allocateHuge
      allocateHuge(buf, reqCapacity);
    }
  }

  // Method must be called inside synchronized(this) { ... } block
  private void allocateNormal(PooledByteBuf<T> buf, int reqCapacity, int normCapacity) {
    if (q050.allocate(buf, reqCapacity, normCapacity)
        || q025.allocate(buf, reqCapacity, normCapacity)
        || q000.allocate(buf, reqCapacity, normCapacity)
        || qInit.allocate(buf, reqCapacity, normCapacity)
        || q075.allocate(buf, reqCapacity, normCapacity)) {
      return;
    }

    // Add a new chunk.
    PoolChunk<T> c = newChunk(pageSize, maxOrder, pageShifts, chunkSize);
    boolean success = c.allocate(buf, reqCapacity, normCapacity);
    assert success;
    qInit.add(c);
  }

  private void incTinySmallAllocation(boolean tiny) {
    if (tiny) {
      allocationsTiny.increment();
    } else {
      allocationsSmall.increment();
    }
  }

  private void allocateHuge(PooledByteBuf<T> buf, int reqCapacity) {
    PoolChunk<T> chunk = newUnpooledChunk(reqCapacity);
    activeBytesHuge.add(chunk.chunkSize());
    buf.initUnpooled(chunk, reqCapacity);
    allocationsHuge.increment();
  }

  void free(PoolChunk<T> chunk, ByteBuffer nioBuffer, long handle, int normCapacity,
      PoolThreadCache cache) {
    if (chunk.unpooled) {
      int size = chunk.chunkSize();
      destroyChunk(chunk);
      activeBytesHuge.add(-size);
      deallocationsHuge.increment();
    } else {
      SizeClass sizeClass = sizeClass(normCapacity);
      if (cache != null && cache.add(this, chunk, nioBuffer, handle, normCapacity, sizeClass)) {
        // cached so not free it.
        return;
      }

      freeChunk(chunk, handle, sizeClass, nioBuffer);
    }
  }

  private SizeClass sizeClass(int normCapacity) {
    if (!isTinyOrSmall(normCapacity)) {
      return SizeClass.Normal;
    }
    return isTiny(normCapacity) ? SizeClass.Tiny : SizeClass.Small;
  }

  void freeChunk(PoolChunk<T> chunk, long handle, SizeClass sizeClass, ByteBuffer nioBuffer) {
    final boolean destroyChunk;
    synchronized (this) {
      switch (sizeClass) {
        case Normal:
          ++deallocationsNormal;
          break;
        case Small:
          ++deallocationsSmall;
          break;
        case Tiny:
          ++deallocationsTiny;
          break;
        default:
          throw new Error();
      }
      destroyChunk = !chunk.parent.free(chunk, handle, nioBuffer);
    }
    if (destroyChunk) {
      // destroyChunk not need to be called while holding the synchronized lock.
      destroyChunk(chunk);
    }
  }

  PoolSubpage<T> findSubpagePoolHead(int elemSize) {
    int tableIdx;
    PoolSubpage<T>[] table;
    if (isTiny(elemSize)) { // < 512
      tableIdx = elemSize >>> 4;
      table = tinySubpagePools;
    } else {
      tableIdx = 0;
      elemSize >>>= 10;
      while (elemSize != 0) {
        elemSize >>>= 1;
        tableIdx++;
      }
      table = smallSubpagePools;
    }

    return table[tableIdx];
  }

  int normalizeCapacity(int reqCapacity) {
    checkPositiveOrZero(reqCapacity, "reqCapacity");

    if (reqCapacity >= chunkSize) {
      return directMemoryCacheAlignment == 0 ? reqCapacity : alignCapacity(reqCapacity);
    }

    if (!isTiny(reqCapacity)) { // >= 512
      // Doubled

      int normalizedCapacity = reqCapacity;
      normalizedCapacity--;
      normalizedCapacity |= normalizedCapacity >>> 1;
      normalizedCapacity |= normalizedCapacity >>> 2;
      normalizedCapacity |= normalizedCapacity >>> 4;
      normalizedCapacity |= normalizedCapacity >>> 8;
      normalizedCapacity |= normalizedCapacity >>> 16;
      normalizedCapacity++;

      if (normalizedCapacity < 0) {
        normalizedCapacity >>>= 1;
      }
      assert directMemoryCacheAlignment == 0
          || (normalizedCapacity & directMemoryCacheAlignmentMask) == 0;

      return normalizedCapacity;
    }

    if (directMemoryCacheAlignment > 0) {
      return alignCapacity(reqCapacity);
    }

    // Quantum-spaced
    if ((reqCapacity & 15) == 0) {
      return reqCapacity;
    }

    // 计算目标容量扩容到第一个16的倍数是多少，比如100扩容后是112 = 16 * 7
    return (reqCapacity & ~15) + 16;
  }

  int alignCapacity(int reqCapacity) {
    int delta = reqCapacity & directMemoryCacheAlignmentMask;
    return delta == 0 ? reqCapacity : reqCapacity + directMemoryCacheAlignment - delta;
  }

  void reallocate(PooledByteBuf<T> buf, int newCapacity, boolean freeOldMemory) {
    if (newCapacity < 0 || newCapacity > buf.maxCapacity()) {
      throw new IllegalArgumentException("newCapacity: " + newCapacity);
    }

    int oldCapacity = buf.length;
    if (oldCapacity == newCapacity) {
      return;
    }

    PoolChunk<T> oldChunk = buf.chunk;
    ByteBuffer oldNioBuffer = buf.tmpNioBuf;
    long oldHandle = buf.handle;
    T oldMemory = buf.memory;
    int oldOffset = buf.offset;
    int oldMaxLength = buf.maxLength;
    int readerIndex = buf.readerIndex();
    int writerIndex = buf.writerIndex();

    allocate(parent.threadCache(), buf, newCapacity);
    if (newCapacity > oldCapacity) {
      memoryCopy(
          oldMemory, oldOffset,
          buf.memory, buf.offset, oldCapacity);
    } else if (newCapacity < oldCapacity) {
      if (readerIndex < newCapacity) {
        if (writerIndex > newCapacity) {
          writerIndex = newCapacity;
        }
        memoryCopy(
            oldMemory, oldOffset + readerIndex,
            buf.memory, buf.offset + readerIndex, writerIndex - readerIndex);
      } else {
        readerIndex = writerIndex = newCapacity;
      }
    }

    buf.setIndex(readerIndex, writerIndex);

    if (freeOldMemory) {
      free(oldChunk, oldNioBuffer, oldHandle, oldMaxLength, buf.cache);
    }
  }

  @Override
  public int numThreadCaches() {
    return numThreadCaches.get();
  }

  @Override
  public int numTinySubpages() {
    return tinySubpagePools.length;
  }

  @Override
  public int numSmallSubpages() {
    return smallSubpagePools.length;
  }

  @Override
  public int numChunkLists() {
    return chunkListMetrics.size();
  }

  @Override
  public List<PoolSubpageMetric> tinySubpages() {
    return subPageMetricList(tinySubpagePools);
  }

  @Override
  public List<PoolSubpageMetric> smallSubpages() {
    return subPageMetricList(smallSubpagePools);
  }

  @Override
  public List<PoolChunkListMetric> chunkLists() {
    return chunkListMetrics;
  }

  private static List<PoolSubpageMetric> subPageMetricList(PoolSubpage<?>[] pages) {
    List<PoolSubpageMetric> metrics = new ArrayList<PoolSubpageMetric>();
    for (PoolSubpage<?> head : pages) {
      if (head.next == head) {
        continue;
      }
      PoolSubpage<?> s = head.next;
      for (; ; ) {
        metrics.add(s);
        s = s.next;
        if (s == head) {
          break;
        }
      }
    }
    return metrics;
  }

  @Override
  public long numAllocations() {
    final long allocsNormal;
    synchronized (this) {
      allocsNormal = allocationsNormal;
    }
    return allocationsTiny.value() + allocationsSmall.value() + allocsNormal + allocationsHuge
        .value();
  }

  @Override
  public long numTinyAllocations() {
    return allocationsTiny.value();
  }

  @Override
  public long numSmallAllocations() {
    return allocationsSmall.value();
  }

  @Override
  public synchronized long numNormalAllocations() {
    return allocationsNormal;
  }

  @Override
  public long numDeallocations() {
    final long deallocs;
    synchronized (this) {
      deallocs = deallocationsTiny + deallocationsSmall + deallocationsNormal;
    }
    return deallocs + deallocationsHuge.value();
  }

  @Override
  public synchronized long numTinyDeallocations() {
    return deallocationsTiny;
  }

  @Override
  public synchronized long numSmallDeallocations() {
    return deallocationsSmall;
  }

  @Override
  public synchronized long numNormalDeallocations() {
    return deallocationsNormal;
  }

  @Override
  public long numHugeAllocations() {
    return allocationsHuge.value();
  }

  @Override
  public long numHugeDeallocations() {
    return deallocationsHuge.value();
  }

  @Override
  public long numActiveAllocations() {
    long val = allocationsTiny.value() + allocationsSmall.value() + allocationsHuge.value()
        - deallocationsHuge.value();
    synchronized (this) {
      val += allocationsNormal - (deallocationsTiny + deallocationsSmall + deallocationsNormal);
    }
    return max(val, 0);
  }

  @Override
  public long numActiveTinyAllocations() {
    return max(numTinyAllocations() - numTinyDeallocations(), 0);
  }

  @Override
  public long numActiveSmallAllocations() {
    return max(numSmallAllocations() - numSmallDeallocations(), 0);
  }

  @Override
  public long numActiveNormalAllocations() {
    final long val;
    synchronized (this) {
      val = allocationsNormal - deallocationsNormal;
    }
    return max(val, 0);
  }

  @Override
  public long numActiveHugeAllocations() {
    return max(numHugeAllocations() - numHugeDeallocations(), 0);
  }

  @Override
  public long numActiveBytes() {
    long val = activeBytesHuge.value();
    synchronized (this) {
      for (int i = 0; i < chunkListMetrics.size(); i++) {
        for (PoolChunkMetric m : chunkListMetrics.get(i)) {
          val += m.chunkSize();
        }
      }
    }
    return max(0, val);
  }

  protected abstract PoolChunk<T> newChunk(int pageSize, int maxOrder, int pageShifts,
      int chunkSize);

  protected abstract PoolChunk<T> newUnpooledChunk(int capacity);

  protected abstract PooledByteBuf<T> newByteBuf(int maxCapacity);

  protected abstract void memoryCopy(T src, int srcOffset, T dst, int dstOffset, int length);

  protected abstract void destroyChunk(PoolChunk<T> chunk);

  @Override
  public synchronized String toString() {
    StringBuilder buf = new StringBuilder()
        .append("Chunk(s) at 0~25%:")
        .append(StringUtil.NEWLINE)
        .append(qInit)
        .append(StringUtil.NEWLINE)
        .append("Chunk(s) at 0~50%:")
        .append(StringUtil.NEWLINE)
        .append(q000)
        .append(StringUtil.NEWLINE)
        .append("Chunk(s) at 25~75%:")
        .append(StringUtil.NEWLINE)
        .append(q025)
        .append(StringUtil.NEWLINE)
        .append("Chunk(s) at 50~100%:")
        .append(StringUtil.NEWLINE)
        .append(q050)
        .append(StringUtil.NEWLINE)
        .append("Chunk(s) at 75~100%:")
        .append(StringUtil.NEWLINE)
        .append(q075)
        .append(StringUtil.NEWLINE)
        .append("Chunk(s) at 100%:")
        .append(StringUtil.NEWLINE)
        .append(q100)
        .append(StringUtil.NEWLINE)
        .append("tiny subpages:");
    appendPoolSubPages(buf, tinySubpagePools);
    buf.append(StringUtil.NEWLINE)
        .append("small subpages:");
    appendPoolSubPages(buf, smallSubpagePools);
    buf.append(StringUtil.NEWLINE);

    return buf.toString();
  }

  private static void appendPoolSubPages(StringBuilder buf, PoolSubpage<?>[] subpages) {
    for (int i = 0; i < subpages.length; i++) {
      PoolSubpage<?> head = subpages[i];
      if (head.next == head) {
        continue;
      }

      buf.append(StringUtil.NEWLINE)
          .append(i)
          .append(": ");
      PoolSubpage<?> s = head.next;
      for (; ; ) {
        buf.append(s);
        s = s.next;
        if (s == head) {
          break;
        }
      }
    }
  }

  @Override
  protected final void finalize() throws Throwable {
    try {
      super.finalize();
    } finally {
      destroyPoolSubPages(smallSubpagePools);
      destroyPoolSubPages(tinySubpagePools);
      destroyPoolChunkLists(qInit, q000, q025, q050, q075, q100);
    }
  }

  private static void destroyPoolSubPages(PoolSubpage<?>[] pages) {
    for (PoolSubpage<?> page : pages) {
      page.destroy();
    }
  }

  private void destroyPoolChunkLists(PoolChunkList<T>... chunkLists) {
    for (PoolChunkList<T> chunkList : chunkLists) {
      chunkList.destroy(this);
    }
  }

  // 这里HeapArena与DirectArena的区别主要是，HeapArena中申请内存的方式是使用字节数组，内存回收也是依赖GC；
  // 而DirectArena中申请内存的方式是使用ByteBuffer，其内存回收方式则是依赖相应的API。
  // 这一点可以通过HeapArena和DirectArena上的泛型参数看出来。
  static final class HeapArena extends PoolArena<byte[]> {

    HeapArena(PooledByteBufAllocator parent, int pageSize, int maxOrder,
        int pageShifts, int chunkSize, int directMemoryCacheAlignment) {
      super(parent, pageSize, maxOrder, pageShifts, chunkSize,
          directMemoryCacheAlignment);
    }

    /**
     * 返回一个长度为size的字节数组
     */
    private static byte[] newByteArray(int size) {
      return PlatformDependent.allocateUninitializedArray(size);
    }

    @Override
    boolean isDirect() {
      return false;
    }

    /**
     * 返回一个PoolChunk对象，该对象中保存了可以使用的内存块，这里是一个字节数组， 该PoolChunk会根据pageSize，pageShifts和chunkSize的配置来分配字节数组的大小
     */
    @Override
    protected PoolChunk<byte[]> newChunk(int pageSize, int maxOrder, int pageShifts,
        int chunkSize) {
      return new PoolChunk<byte[]>(this, newByteArray(chunkSize), pageSize, maxOrder, pageShifts,
          chunkSize, 0);
    }

    /**
     * 返回一个不提供池化功能的PoolChunk
     */
    @Override
    protected PoolChunk<byte[]> newUnpooledChunk(int capacity) {
      return new PoolChunk<byte[]>(this, newByteArray(capacity), capacity, 0);
    }

    /**
     * 这里由于是byte数组，因而其销毁方式依赖于GC
     */
    @Override
    protected void destroyChunk(PoolChunk<byte[]> chunk) {
      // Rely on GC.
    }

    /**
     * 这里会创建一个PooledByteBuf对象，如果存在UNSAFE对象，则使用UNSAFE来创建你字节数组， 如果不存在，则直接new一个
     */
    @Override
    protected PooledByteBuf<byte[]> newByteBuf(int maxCapacity) {
      return HAS_UNSAFE ? PooledUnsafeHeapByteBuf.newUnsafeInstance(maxCapacity)
          : PooledHeapByteBuf.newInstance(maxCapacity);
    }

    /**
     * 将一个字节数组上的数据复制到林那个一个字节数组上
     */
    @Override
    protected void memoryCopy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int length) {
      if (length == 0) {
        return;
      }

      System.arraycopy(src, srcOffset, dst, dstOffset, length);
    }
  }

  /**
   * 这里DirectArena与HeapArena的区别在于其泛型参数不一样，也就是说，两者使用的数据存储容器不一样。 这里DirectArena使用呢的是ByteBuffer
   */
  static final class DirectArena extends PoolArena<ByteBuffer> {

    DirectArena(PooledByteBufAllocator parent, int pageSize, int maxOrder,
        int pageShifts, int chunkSize, int directMemoryCacheAlignment) {
      super(parent, pageSize, maxOrder, pageShifts, chunkSize,
          directMemoryCacheAlignment);
    }

    @Override
    boolean isDirect() {
      return true;
    }

    /**
     * 该方法只在下面用到了，其他用到的位置只是单元测试
     */
    // mark as package-private, only for unit test
    int offsetCacheLine(ByteBuffer memory) {
      // We can only calculate the offset if Unsafe is present as otherwise directBufferAddress(...) will
      // throw an NPE.
      // 这里PlatformDependent.directBufferAddress(memory)将会返回memory所代表的ByteBuffer中
      // 存储数据所使用的内存地址起始位置
      int remainder = HAS_UNSAFE
          ? (int) (PlatformDependent.directBufferAddress(memory) & directMemoryCacheAlignmentMask)
          : 0;

      // offset = alignment - address & (alignment - 1)
      return directMemoryCacheAlignment - remainder;
    }

    @Override
    protected PoolChunk<ByteBuffer> newChunk(int pageSize, int maxOrder,
        int pageShifts, int chunkSize) {
      if (directMemoryCacheAlignment == 0) {
        return new PoolChunk<ByteBuffer>(this,
            allocateDirect(chunkSize), pageSize, maxOrder,
            pageShifts, chunkSize, 0);
      }
      final ByteBuffer memory = allocateDirect(chunkSize
          + directMemoryCacheAlignment);
      return new PoolChunk<ByteBuffer>(this, memory, pageSize,
          maxOrder, pageShifts, chunkSize,
          offsetCacheLine(memory));
    }

    @Override
    protected PoolChunk<ByteBuffer> newUnpooledChunk(int capacity) {
      if (directMemoryCacheAlignment == 0) {
        return new PoolChunk<ByteBuffer>(this,
            allocateDirect(capacity), capacity, 0);
      }
      final ByteBuffer memory = allocateDirect(capacity
          + directMemoryCacheAlignment);
      return new PoolChunk<ByteBuffer>(this, memory, capacity,
          offsetCacheLine(memory));
    }

    private static ByteBuffer allocateDirect(int capacity) {
      return PlatformDependent.useDirectBufferNoCleaner() ?
          PlatformDependent.allocateDirectNoCleaner(capacity) : ByteBuffer.allocateDirect(capacity);
    }

    @Override
    protected void destroyChunk(PoolChunk<ByteBuffer> chunk) {
      if (PlatformDependent.useDirectBufferNoCleaner()) {
        PlatformDependent.freeDirectNoCleaner(chunk.memory);
      } else {
        PlatformDependent.freeDirectBuffer(chunk.memory);
      }
    }

    @Override
    protected PooledByteBuf<ByteBuffer> newByteBuf(int maxCapacity) {
      if (HAS_UNSAFE) {
        return PooledUnsafeDirectByteBuf.newInstance(maxCapacity);
      } else {
        return PooledDirectByteBuf.newInstance(maxCapacity);
      }
    }

    @Override
    protected void memoryCopy(ByteBuffer src, int srcOffset, ByteBuffer dst, int dstOffset,
        int length) {
      if (length == 0) {
        return;
      }

      if (HAS_UNSAFE) {
        PlatformDependent.copyMemory(
            PlatformDependent.directBufferAddress(src) + srcOffset,
            PlatformDependent.directBufferAddress(dst) + dstOffset, length);
      } else {
        // We must duplicate the NIO buffers because they may be accessed by other Netty buffers.
        src = src.duplicate();
        dst = dst.duplicate();
        src.position(srcOffset).limit(srcOffset + length);
        dst.position(dstOffset);
        dst.put(src);
      }
    }
  }
}
